﻿//--------------------------------------------------------------------------------------------------------------------- 
// <copyright file="CreateUser.cs" company="Collaboris Ltd.">
//   Copyright (c) Collaboris Ltd. All rights reserved.
// </copyright>
// <summary>
//   Creates a User.
// </summary>
//---------------------------------------------------------------------------------------------------------------------
namespace Collaboris.Saf.Actions.Wss.Security
{
    using System;
    using System.Collections.Generic;

    using Engine;
    using Entities;
    using Microsoft.SharePoint;

    using SharePoint.Wss.Attributes;

    using Utils;

    [CollaborisClass(
        Title = "Creates a Site User",
        ReleaseStatus = ReleaseStatus.Beta,
        DisableDocumentation = false,
        ContactAuthorEmail = "Support@Collaboris.co.uk",
        Summary = "Creates a new user in the site (see documentation on SPWeb.SiteUsers) and can optionally make the user site collection admin.",
        Remarks = "This does  not add the user to any specific groups in the current web or parent site.",
        ClassGuid = "{156E17A5-A7BA-4e1f-8A48-1BC341A7C656}",
        UndoImplemented = false
    )
    ]
    public class CreateUser : WSSActionBase<UserInfo>
    {

        private bool userIsDirty;

        /// <summary>
        /// Does the action.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="entity">The entity.</param>
        /// <returns></returns>
        protected override ProcessStatus DoAction(SafContext context, UserInfo entity)
        {
            this.ProcessUser(entity, ProcessMode.Do, context);

            return ProcessStatus.Success;
        }

        /// <summary>
        /// Undoes the action.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="entity">The entity.</param>
        /// <returns></returns>
        protected override ProcessStatus UndoAction(SafContext context, UserInfo entity)
        {
            // temporary - due to the fact it doesn't work!
            return ProcessStatus.Skip;

            // attempt to get Previous User (or not)
            //UserInfo previousUser = this.GetProperty<UserInfo>(TargetWeb, this.GetUniqueKey(entity));
            UserInfo previousUser = context.ActionProperties.Get<UserInfo>(this.GetUniqueKey(entity));

            if (previousUser == null)
            {
                // the user never previously existed so lets delete them
                if (TargetWeb.SiteUsers[entity.LoginName] != null)
                {
                    TargetWeb.SiteUsers.Remove(entity.LoginName);
                }
            }
            else
            {
                this.ProcessUser(previousUser, ProcessMode.Undo, context);
            }

            // delete the old Property
            this.DeleteProperty(TargetWeb, this.GetUniqueKey(entity));

            return ProcessStatus.Success;
        }


        /// <summary>
        /// Checks the Data entity used by the Action for Validity
        /// </summary>
        /// <param name="context">The <see cref="SafContext"/> passed in by the SAF Engine.</param>
        /// <param name="entity">The data entity to Validate.</param>
        /// <param name="errors">List of <see cref="ValidationError"/>s</param>
        /// <param name="mode"><see cref="ProcessMode"/>signalling wheter we are Doing, or Undoing the action.</param>
        /// <returns>false if invalid; otherwise true.</returns>
        protected override bool IsValid(SafContext context, UserInfo entity, ref List<ValidationError> errors, ProcessMode mode)
        {
            
            int currentCount = errors.Count;

            // Validate each object now.    
            ValidateStringForNull(entity.LoginName, "UserInfo.LoginName", ref errors);
            ValidateStringForNull(entity.Name, "UserInfo.Name", ref errors);
            if (entity.MakeAdmin.HasValue == false)
                entity.MakeAdmin= false;

            // Have we added errors ?
            return errors.Count == currentCount;
        }

        /// <summary>
        /// Processes the user.
        /// </summary>
        /// <param name="entity">The entity.</param>
        private void ProcessUser(UserInfo entity, ProcessMode mode, SafContext context)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "CreateUser", "ProcessUser");

            string currentUser = entity.LoginName;
            SPUser user = null;

            if (mode == ProcessMode.Do)
            {
                try
                {
                    user = TargetWeb.SiteUsers[entity.LoginName];
                }
                catch
                {}

                if (user != null)
                {
                    SaveOldUser(user, entity, context);
                }
            }

            //Attempt to add the user in as a site user
            user = this.EnsureUser(entity);

            //should never happen as this will usually throw an exception
            if (user == null)
            {
                throw new NullReferenceException("Could not locate (or create) this user '" + currentUser + "'");
            }

            //try
            //{
            //    this.MakeAdmin(entity, currentUser, user);

            //    // commit changes.
            //    if (userIsDirty)
            //        user.Update();

            //}
            //catch (Exception ex)
            //{
            //    TraceHelper.TraceException(TraceSwitch, "CreateUser", "ProcessUser", ex);
            //}            

            TraceHelper.TraceMethodEnd(TraceSwitch, "CreateUser", "ProcessUser");
        }

        /// <summary>
        /// Saves the old user.
        /// </summary>
        /// <param name="user">The user.</param>
        /// <param name="entity">The entity.</param>
        private void SaveOldUser(SPUser user, UserInfo entity, SafContext context)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "CreateUser", "SaveOldUser");

            UserInfo oldUser = new UserInfo();
            oldUser.EmailAddress = user.Email;
            oldUser.LoginName = user.LoginName;
            oldUser.Name = user.Name;
            oldUser.Notes = user.Notes;
            oldUser.MakeAdmin = user.IsSiteAdmin;
            
            context.ActionProperties.Add(this.GetUniqueKey(entity), oldUser);
            //this.SaveProperty(TargetWeb, this.GetUniqueKey(entity), oldUser);
            
            TraceHelper.TraceMethodEnd(TraceSwitch, "CreateUser", "SaveOldUser");
        }
        
        /// <summary>
        /// Creates the new user.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <returns></returns>
        private SPUser EnsureUser(UserInfo entity)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "CreateUser", "EnsureUser");
            this.Trace.TraceInfo("UserInfo '{0}' doesn't exist. - adding", entity.LoginName);
            SPUser user;
            try
            {
                //this.TargetWeb.SiteUsers.Add(entity.LoginName, entity.EmailAddress, entity.Name, entity.Notes);

                //user = this.TargetWeb.AllUsers[entity.LoginName];

                user = this.TargetWeb.EnsureUser(entity.LoginName);

                if (entity.MakeAdmin.HasValue)
                    user.IsSiteAdmin = entity.MakeAdmin.Value;

                user.Email = entity.EmailAddress;
                user.Notes = entity.Notes;
                user.Update();
            }
            catch (Exception ex)
            {
                TraceHelper.TraceError(TraceSwitch, "CreateUser", "Unable to create this new user : '{0}'", 
                    entity.LoginName);

                throw;
            }
            
            TraceHelper.TraceMethodEnd(TraceSwitch, "CreateUser", "EnsureUser");

            return user;
        }

        /// <summary>
        /// Makes the user and administrator.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="currentUser">The current user.</param>
        /// <param name="user">The user.</param>
        private void MakeAdmin(UserInfo entity, string currentUser, SPUser user)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "CreateUser", "MakeAdmin");
            if (! entity.MakeAdmin.HasValue)
            {
                return;    
            }

            if (entity.MakeAdmin.Value)
            {
                TraceHelper.TraceVerbose(TraceSwitch, "AddSiteAdmins", "Adding Admin privileges to User '{0}'.", currentUser);
                user.IsSiteAdmin = true;

            }
            else
            {
                TraceHelper.TraceVerbose(TraceSwitch, "AddSiteAdmins", "Removing Admin privileges from User '{0}'.", currentUser);
                user.IsSiteAdmin = false;
            }

            userIsDirty = true;
            
            TraceHelper.TraceMethodEnd(TraceSwitch, "CreateUser", "MakeAdmin");
        }
    }
   
}
